#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <aos/kernel.h>
#include <aos/list.h>
#include "lwip/def.h"
#include "lwip/err.h"
#include "vfs.h"
#include "memheap.h"
#include "dconfig.h"
#include "aoxml.h"
#include "aolisthead.h"

#define MAX_KEY_SIZE	64

#define MAX_SECTION_SIZE	128
#ifdef WIN32
#define DEVICECONF_PATH   "D:\\xmesh\\etc"
#define DEVICE_CONFIG_FILE	"D:\\xmesh\\etc\\dconfig.conf"
#define AoSnprintf _snprintf
#define AoStrcasecmp stricmp
#else
#define DEVICECONF_PATH   "/mmc/deviceconf"
#define DEVICE_CONFIG_FILE	"/mmc/deviceconf/dconfig.conf"
#define AoSnprintf snprintf
#define AoStrcasecmp stricmp
#endif

typedef struct _AoConfigKey{
	AoListHead				list;
	u8_t *					name;
	u8_t *					value;	
//	struct AoListHead		blocks;
}AoConfigKey;

typedef struct _AoConfigSection{
	AoListHead					list;
	AoListHead					link;
	u8_t*						name;
	u8_t*						value;
	AoListHead					keys;
	AoListHead					sections;
	void*						config;
//	void*						sub;	
	struct _AoConfigSection*	parent;
}AoConfigSection;

typedef int (*SectionFunc)(u8_t * name,u8_t *value, void *param);
typedef int (*ConfigFunc)(AoConfigSection* section, void *param);
typedef int (*SectionLoadFunc)(AoConfigSection* section, void *param);

typedef struct AoConfig{
	void*					home;
	int						alloc;
	SectionLoadFunc			load;
	void *					param;
	aos_mutex_t				lock;
	AoConfigSection*		rs;
	u8_t *					filename;
//	int						change;
	AoListHead				freeKeys;
	AoListHead				freeSections;
	AoListHead				root;
	AoConfigSection*		last;
	AoListHead				sections;
	AoListHead*				hash;
}AoConfig;



static AoConfig * AoConfigConstruct(void * home,u32_t max);
static int  AoConfigDestruct(AoConfig * config);
static AoConfigSection* AoConfigSetSection(AoConfig * config,const u8_t * name);
static AoConfigKey *AoConfigSetKey(AoConfigSection *section,u8_t * name,u8_t * value);
static u8_t *AoSectionGetString(AoConfigSection * section,const u8_t * key);
static AoBool AoConfigReplaceKey(AoConfigSection *section,u8_t * name,u8_t * value);
static AoConfigSection *AoConfigGetRoot(AoConfig * config);
static AoConfigSection *AoConfigSetRoot(AoConfig * config,u8_t * name);
static AoConfigSection *AoSectionGetSection(AoConfigSection * section,u8_t * name);
static AoConfigSection *AoSectionSetSection(AoConfigSection * s,u8_t * name);
static int		AoConfigLoadXml(AoConfig * config,u8_t * buf,int len,
					 SectionLoadFunc func,void * param);
static int		AoConfigLoadXmlFromFile(AoConfig * config,u8_t * filename,
						u8_t *exclude,SectionLoadFunc func,void * param);
static int	AoConfigWriteXmlToFile(AoConfig * config,u8_t * filename,u8_t * ex);
static int AoSectionWriteXmlToBuffer(AoConfigSection* section,u8_t *buf,int len);
static int	AoConfigWriteXmlToBuffer(AoConfig * config,u8_t *buf,int len,u8_t * ex);

#if 0
static AoConfigSection  * AoConfigSectionConstruct(void * home);
static int  AoConfigSectionDestruct(AoConfigSection * section);

static int AoConfigSetFile(AoConfig * config,u8_t * filename);
static int	AoConfigLoad(AoConfig * config,u8_t * buf,int len,
					 u8_t * ex,SectionLoadFunc func,void * parm);

static int	AoConfigLoadFromFile(AoConfig * config,u8_t * filename,
					 u8_t *exclude,SectionLoadFunc func,void * parm);

static u8_t *	AoConfigGetSectionName(AoConfigSection * section);
static AoConfigSection *AoConfigGetSection(AoConfig * config,const u8_t * name);
static int AoConfigKeyRemove(AoConfigSection *section,const u8_t * name);
static int AoConfigKeyRemoveAll(AoConfigSection *section);
static int AoConfigSectionDoAll(AoConfigSection * section,SectionFunc func,void * param);
static int AoConfigDoAll(AoConfig * config,ConfigFunc func,void * param);

static int AoConfigGetString(AoConfigSection * section,const u8_t * key,u8_t ** value);
static int AoConfigGetInteger(AoConfigSection * section,const u8_t * key,int * value);
static int AoConfigGetUint16(AoConfigSection * section,const u8_t * key,u16_t * value);
static int AoConfigGetIpv4(AoConfigSection * section,const u8_t * key,u32_t * value);
static int AoConfigGetBool(AoConfigSection * section,const u8_t * name,AoBool * value);

static int AoSectionGetInteger(AoConfigSection * section,const u8_t * key);
static u32_t AoSectionGetIpv4(AoConfigSection * section,const u8_t * key);
static AoBool AoSectionGetBool(AoConfigSection * section,const u8_t * name);

static AoBool AoConfiCompareKey(AoConfigSection *section,u8_t * name,u8_t * value);
static int AoConfigCopy(AoConfig * dest,AoConfig * src);
static int AoConfigSectionRemove(AoConfigSection * section);


static AoConfigSection *AoConfigGetNext(AoConfigSection * section);

static AoConfigSection *AoSectionGetFirst(AoConfigSection * section);
static AoConfigSection *AoSectionGetNext(AoConfigSection * section);
static AoConfigSection *AoSectionGetParent(AoConfigSection * section);
static AoBool AoSectionHaveSection(AoConfigSection * section);
static AoBool AoSectionHaveKey(AoConfigSection * section);

static AoConfigKey * AoKeyGetFirst(AoConfigSection * section);
static AoConfigKey *AoKeyGetNext(AoConfigSection * section,AoConfigKey * key);

static int AoSectionKeyCompare(AoConfigSection * s1,AoConfigSection * s2,u8_t *name);
static int AoSectionUpdate(AoConfigSection * dest,AoConfigSection * src);
#endif

static AoConfig * cfg=NULL;



void device_config_file_check(void)
{
    aos_dir_t *dp;
    int ret = -1;

	
	dp = (aos_dir_t *)aos_opendir(DEVICECONF_PATH);
	if (!dp) 
	{
		ret = aos_mkdir(DEVICECONF_PATH);
		if(0 == ret)
		{
//			printf("DEVICECONF_PATH create suc\n");
		}

	}

    if(dp)
	{
        aos_closedir(dp);
    }
}

// static int AoFprintf(int fd,const char *format,...)
// {
// 	char buf[1024];
// 	int i;
// 	va_list args;
// 	va_start(args, format);
// #ifdef WIN32
// 	i = _snprintf(buf,sizeof(buf),format, args);
// #else
// 	i = snprintf(buf,sizeof(buf),format, args);
// #endif
// 	va_end(args);
// 	if(i<0)
// 		return i;
// 	return aos_write(fd,buf,i);
// }

int device_config_init()
{
	int ret;
	AoBool update=AO_FALSE;

	device_config_file_check();
	cfg = AoConfigConstruct(NULL,0);
	if(!cfg)
		return ERR_MEM;
	ret = AoConfigLoadXmlFromFile(cfg,(u8_t *)DEVICE_CONFIG_FILE,NULL,NULL,NULL);
	if(ret!=ERR_OK)
	{
		cfg->rs = AoConfigSetRoot(cfg,(u8_t *)"device");
		update = AO_TRUE;
	}
	cfg->rs = AoConfigGetRoot(cfg);
	if(!cfg->rs)
	{
		cfg->rs = AoConfigSetRoot(cfg,(u8_t *)"device");
		update = AO_TRUE;
	}
	if(update)
	{
		AoConfigWriteXmlToFile(cfg,(u8_t *)DEVICE_CONFIG_FILE,NULL);
	}
	return ERR_OK;
}

void device_config_end()
{
	if(cfg)
	{
		AoConfigDestruct(cfg);
		cfg = NULL;
	}
}


int  device_config_set_value(char * root,char * sec,char * name,const char * value)
{
//	char key[512];
	AoBool update=AO_FALSE,res;
	AoConfigSection* parent,*section;

	if(!cfg)
		return ERR_MEM;
//	AoSnprintf(key,sizeof(key),"%s:%s",root,section);
	parent = AoSectionGetSection(cfg->rs,(u8_t *)root);
	if(!parent)
	{
		parent = AoSectionSetSection(cfg->rs,(u8_t *)root);
		update = AO_TRUE;
	}
	section = AoSectionGetSection(parent,(u8_t *)sec);
	if(!section)
	{
		section = AoSectionSetSection(parent,(u8_t *)sec);
		update = AO_TRUE;
	}
	res = AoConfigReplaceKey(section,(u8_t *)name,(u8_t *)value);
	if(res)
	{
		update = AO_TRUE;
	}
	if(update)
	{
		AoConfigWriteXmlToFile(cfg,(u8_t *)DEVICE_CONFIG_FILE,NULL);
		return CONFIG_UPDATE;
	}
	return ERR_OK;
}
int  device_config_get_value(char * root,char * sec,char * name,char ** value)
{
	AoConfigSection* section;
	* value = NULL;
	if(!cfg)
		return ERR_MEM;
	section = AoSectionGetSection(cfg->rs,(u8_t *)root);
	if(!section)
	{
		return -1;
	}
	section = AoSectionGetSection(section,(u8_t *)sec);
	if(!section)
	{
		return -1;
	}
	* value = (char *)AoSectionGetString(section,(u8_t *)name);
	return ERR_OK;
}

AoConfig * AoConfigConstruct(void * home,u32_t max)
{
//	void* home=NULL;
//	int i;
	AoConfig * config;
	int alloc=0;
	if(!home)
	{
		memHeapCreate(&home,0);
		if(!home)
			return NULL;
		alloc = 1;
	}

	config = (AoConfig*)memHeapAlloc(&home,sizeof(AoConfig));
	if(!config)
	{
		memHeapRelease(&home);
		return NULL;
	}
	config->home = home;
	config->alloc = alloc;
	aos_mutex_new(&config->lock);
	AO_LIST_HEAD_INIT(&config->sections);
	AO_LIST_HEAD_INIT(&config->root);
	AO_LIST_HEAD_INIT(&config->freeKeys);
	AO_LIST_HEAD_INIT(&config->freeSections);
	config->last = NULL;
//	config->root = NULL;
	config->hash = NULL;
	config->load = NULL;
	config->param = NULL;
	return config;
	
}

int AoConfigDestruct(AoConfig * config)
{
	AoListHead *head,*__i;
	AoConfigSection * section;
//	AoConfig * t;
	
	if(!config)
		return ERR_ARG;
	head =  &config->sections; 
	__i = head->next;
	while(__i!=head)
	{
		section = (AoConfigSection*)aos_container_of(__i,AoConfigSection,link);
		__i = __i->next;
		
	}
	if(config->alloc)
	{
		memHeapRelease(&config->home);
	}
	return ERR_OK;
}
#if 0
AoConfigSection  * AoConfigSectionConstruct(void * home)
{
	AoConfigSection * section;
	AoConfig * config;

	if(!home)
	{
		memHeapCreate (&home,512);
		if(!home)
			return NULL;
	}

	section = (AoConfigSection*)memHeapAlloc(&home,sizeof(AoConfigSection));
	if(!section)
	{
		memHeapRelease(&home);
		return NULL;
	}
	config = (AoConfig*)memHeapAlloc(&home,sizeof(AoConfig));
	if(!config)
	{
		memHeapRelease(&home);
		return NULL;
	}
	memset(config,0,sizeof(AoConfig));
	config->alloc = AO_TRUE;
	config->home = home;
	AO_LIST_HEAD_INIT(&config->freeKeys);
	AO_LIST_HEAD_INIT(&config->root);
	AO_LIST_HEAD_INIT(&config->sections);

	section->config = (void*)config;
	section->name = NULL;
//	section->sub = NULL;
	AO_LIST_HEAD_INIT(&section->keys);
	AO_LIST_HEAD_INIT(&section->sections);
	AO_LIST_HEAD_INIT(&section->link);
	AO_LIST_HEAD_INIT(&section->list);
	return section;
}
int  AoConfigSectionDestruct(AoConfigSection * section)
{
	AoConfig * config = (AoConfig *)section->config;
	if(config->alloc)
		memHeapRelease(&config->home);
	return ERR_OK;
}

static u8_t * AoReadLine(u8_t **pcFileBuf,u8_t *pcFileTail)
{
	u8_t *line=NULL,* p = *pcFileBuf;
	while(p>=pcFileTail&&(*p=='\n'||*p=='\r'||*p=='\t'||*p==' '))
		p++;
	if(p>=pcFileTail)
		return NULL;
	line=p;
	while(p!=pcFileTail&&*p!='\n'&&*p!='\r')
		p++;
	*p++='\0';
	*pcFileBuf = p;
	return line;
}

static AoBool AoConfigCompareSection(AoConfigSection* section,u8_t *name)
{
	u8_t * p = section->name;
	if(!section||!name)
		return AO_FALSE;
	while(*p&&*p==*name)
	{
		p++;
		name++;
	}
	if(*p=='\0')
	{
		if(*name=='\0')
		{
			return AO_TRUE;
		}
		
	}else if(*name=='\0')
	{
		if(*p=='-')
			return AO_TRUE;
	}
	return AO_FALSE;
}
u8_t *AoConfigGetSectionName(AoConfigSection * section)
{
	return section->name;
}
AoConfigSection *AoConfigGetSection(AoConfig * config,const u8_t * name)
{
	AoListHead *head,*__i;
	AoConfigSection * section;
	if(!config||!name)
		return NULL;
	head = &config->sections;
	__i = head->next;
	while(__i!=head)
	{
		section = (AoConfigSection*)aos_container_of(__i,AoConfigSection,link);
		if(strcmp(section->name,name)==0)
		{
			return section;
		}
		__i = __i->next;
	}
	return NULL;
	
}
#endif


AoConfigSection* AoConfigSetSection(AoConfig * config,const u8_t * name)
{
	AoConfigSection * section=NULL;
//	AoListHead *__i,*head;
	// u32_t hash=0;
	u32_t size = MAX_SECTION_SIZE;

	if(!config||!name)
		return NULL;

	if((sizeof(AoConfigSection)+strlen((char *)name)+1)>size)
	{
		size = (sizeof(AoConfigSection)+strlen((char *)name)+1);
		section = (AoConfigSection*)memHeapAlloc(&config->home,size);
	}else{
		if(AoListHeadEmpty(&config->freeSections))
		{
			section = (AoConfigSection*)memHeapAlloc(&config->home,size);
		}else{
			section = (AoConfigSection *)config->freeSections.prev;
			AoListHeadDel(&section->list);
		}
	}
	if(!section)
	{
		return NULL;
	}
	section->name = (u8_t *)((u8_t *)section+sizeof(AoConfigSection));
	strcpy((char *)section->name,(char *)name);

	AO_LIST_HEAD_INIT(&section->keys);
//	section->sub = NULL;
	section->parent = NULL;
	section->value = NULL;
	section->config = (void*)config;
#if 0
	if(AoListHeadEmpty(&config->sections))
	{
		config->last = section;
	}
#endif	
	AO_LIST_HEAD_INIT(&section->sections);
	AO_LIST_HEAD_INIT(&section->list);
	AO_LIST_HEAD_INIT(&section->link);
	AoListHeadAdd(&section->link,&config->sections);

	return section;
}
#if 0
int AoConfigSectionRemove(AoConfigSection * section)
{
	AoListHead *head,*__i;
	AoConfig * config = (AoConfig *)section->config;
	AoConfigKey * key;
	AoConfigSection* s;

	if(!section||!config)
		return ERR_ARG;
	s = section;

	for(;;)
	{
		head = &s->keys;
		__i = head->prev;
		while(head!=__i)
		{
			key = (AoConfigKey *)__i;
			__i = __i->prev;
			AoListHeadDel(&key->list);
			AO_LIST_HEAD_INIT(&key->list);
			AoListHeadAdd(&key->list,&config->freeKeys);
		}
		
//		if(!section->parent||__i==&section->parent->sections)
		if(AoListHeadEmpty(&s->sections))
		{
			do{
				if(!s->parent||s==section)
				{
					if(s==section)
					{
						AoListHeadDel(&s->link);
						AoListHeadDel(&s->list);
						AO_LIST_HEAD_INIT(&s->list);
						AoListHeadAdd(&s->list,&config->freeSections);
					}
					return ERR_OK;
				}
				__i = &s->list;
				__i = __i->prev;
				AoListHeadDel(&s->link);
				AoListHeadDel(&s->list);
				AO_LIST_HEAD_INIT(&s->list);
				AoListHeadAdd(&s->list,&config->freeSections);
				s = s->parent;
				
			}while(__i==&s->sections);
			s = (AoConfigSection* )__i;
			
		}else{
			__i = &s->sections;
			__i = __i->prev;
			s = (AoConfigSection* )__i;
			
		}
	}
	
#if 0
	AoListHeadDel(&section->link);
	AoListHeadDel(&section->list);
	AO_LIST_HEAD_INIT(&section->list);
	AoListHeadAdd(&section->list,&config->freeSections);
#endif
//	memHeapFreePtr(&config->home,section);
	return ERR_OK;
}

AoBool AoConfiCompareKey(AoConfigSection *section,u8_t * name,u8_t * value)
{
	AoListHead *head ,*__i;
	if(!section||!name||!value)
		return AO_FALSE;
	head = __i =&section->keys;
	do{
		__i = __i->next;
		if(__i==head)
		{
			return AO_FALSE;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,name));
	if(0==strcmp(((AoConfigKey*)__i)->value,value))
		return AO_TRUE;
	return AO_FALSE;
}
#endif

AoBool AoConfigReplaceKey(AoConfigSection *section,u8_t * name,u8_t * value)
{
	AoListHead *head ,*__i;
//	AoConfigKey * key;
	AoConfig * config;
	if(!section||!name)
		return AO_FALSE;
	config = (AoConfig * )section->config;
//	config = (AoConfig * )section->home;
	head = __i =&section->keys;
	do{
		__i = __i->next;
		if(__i==head)
		{
			__i=NULL;
			break;
		}
	}while(0!=strcmp((char *)((AoConfigKey*)__i)->name,(char *)name));
	if(__i)
	{
		if(value && 0==strcmp((char *)((AoConfigKey*)__i)->value,(char *)value))
		{
			return AO_FALSE;
		}
		if(!value||!(*value))
		{
			AoListHeadDel(__i);
			AO_LIST_HEAD_INIT(__i);
			AoListHeadAdd(__i,&config->freeKeys);
			return AO_TRUE;
		}
		if((strlen((char *)name)+strlen((char *)value))<(MAX_KEY_SIZE-2-sizeof(AoConfigKey)))
		{
			strcpy((char *)((AoConfigKey*)__i)->value,(char *)value);
			return AO_TRUE;
		}
		AoListHeadDel(__i);
		AO_LIST_HEAD_INIT(__i);
		AoListHeadAdd(__i,&config->freeKeys);
	}
	if(AoConfigSetKey(section,name,value))
	{
		return AO_TRUE;	
	}
	
	return AO_FALSE;
}

AoConfigKey *AoConfigGetKey(AoConfigSection *section,u8_t * name)
{
	AoListHead *head ,*__i;
	if(!section||!name)
		return NULL;
	head = __i =&section->keys;
	do{
		__i = __i->next;
		if(__i==head)
		{
			return NULL;
		}
	}while(0!=strcmp((char *)((AoConfigKey*)__i)->name,(char *)name));

	return (AoConfigKey *)__i;
	
}

AoConfigKey *AoConfigSetKey(AoConfigSection *section,u8_t * name,u8_t * value)
{
	AoConfigKey *key;
	AoConfig * config;
	unsigned nl,l;
	if(!section||!name||!value)
		return NULL;
//	printf("set %s=%s\n",name,value);
	config = (AoConfig *)section->config;
	nl = strlen((char *)name)+1;
	l = strlen((char *)value)+1;

	if((nl+l)>(MAX_KEY_SIZE-sizeof(AoConfigKey)))
	{
		l = nl+l+sizeof(AoConfigKey);
		key = (AoConfigKey*)memHeapAlloc(&config->home,l);
	}else{
		if(AoListHeadEmpty(&config->freeKeys))
		{
			key = (AoConfigKey*)memHeapAlloc(&config->home,MAX_KEY_SIZE);
		}else{
			key = (AoConfigKey *)config->freeKeys.prev;
			AoListHeadDel(&key->list);
			
		}
	}
	if(!key)
	{
		return NULL;
	}
	key->name = (u8_t*)((u8_t*)key+sizeof(AoConfigKey));
	strcpy((char *)key->name,(char *)name);
	key->value = key->name+nl;
	strcpy((char *)key->value,(char *)value);

	AO_LIST_HEAD_INIT(&key->list);
	AoListHeadAdd(&key->list,&section->keys);
	return key;
}
#if 0
int AoConfigKeyRemove(AoConfigSection *section,const u8_t * name)
{
	AoListHead *head ,*__i;
	AoConfig * config = (AoConfig *)section->config;
//	AoConfigKey *key;

	if(!section||!name||!config)
		return ERR_ARG;
	head = __i =&section->keys;
	do{
		__i = __i->next;
		if(__i==head)
		{
			return ERR_ARG;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,name));
//	key = (AoConfigKey *)__i;
	AoListHeadDel(__i);
	AO_LIST_HEAD_INIT(__i);
	AoListHeadAdd(__i,&config->freeKeys);
//	config = (AoConfig *)section->home;
//	memHeapFreePtr(&config->home,__i);
	return ERR_OK;
}

int AoConfigKeyRemoveAll(AoConfigSection *section)
{
	AoListHead *head ,*__i,*__j;
	AoConfig * config = (AoConfig *)section->config;
//	AoConfigKey *key;

	if(!section||!config)
		return ERR_ARG;
	head = &section->keys;
	__i = head->next;
	while(head!=__i)
	{
		__j = __i;
		__i = __i->next;
		AoListHeadDel(__j);
		AO_LIST_HEAD_INIT(__j);
		AoListHeadAdd(__j,&config->freeKeys);
	}
	
	return ERR_OK;
}
#endif
int AoConfigCpySection(AoConfigSection* dest,AoConfigSection * src)
{
	AoListHead *head,*__i;
	if(!dest||!src)
		return ERR_ARG;
	head =&src->keys;
	__i = head->next;
	while(__i!=head)
	{
		if(!AoConfigGetKey(dest,((AoConfigKey*)__i)->name))
		{
			if(!AoConfigSetKey(dest,((AoConfigKey*)__i)->name,((AoConfigKey*)__i)->value))
			{
				return ERR_ARG;
			}
		}
		__i=__i->next;
	}
	return ERR_OK;
}
#if 0
int AoConfigCopy(AoConfig * dest,AoConfig * src)
{
	AoListHead *head,*__i;
	u8_t * p;
	u8_t name[64];
	AoConfigSection * section;
	if(!dest||!src)
		return ERR_ARG;
	head = &src->sections;
	__i = head->next;
	while(__i!=head)
	{
		p = ((AoConfigSection*)__i)->name;
		while(*p>='0'&&*p<='9')
		{
			p++;
		}
		if(*p=='-')
		{
			
			p++;
			AoSnprintf(name,sizeof(name),"0-%s",p);
			section = AoConfigGetSection(dest,name);
			if(section)
			{
				section = AoConfigGetSection(dest,((AoConfigSection*)__i)->name);
				if(!section)
				{
					__i=__i->next;
					continue;
				}
			}
			
		}else{
			section = AoConfigGetSection(dest,((AoConfigSection*)__i)->name);
		}
		
		if(!section)
		{
			if(ERR_OK!=AoConfigCpySection(AoConfigSetSection(dest,((AoConfigSection*)__i)->name),(AoConfigSection*)__i))
			{
				return ERR_ARG;
			}
		}else{
			if(ERR_OK!=AoConfigCpySection(section,(AoConfigSection*)__i))
			{
				return ERR_ARG;
			}
		}
		__i=__i->next;
	}
	return ERR_OK;
}
int AoConfigSetFile(AoConfig * config,u8_t * filename)
{
	int i;
	i = strlen(filename)+1;

	config->filename = memHeapAlloc(&config->home,i);
	if(!config->filename)
	{
		return ERR_MEM;
	}
	memcpy(config->filename,filename,i);
	return ERR_OK;
}


int AoConfigGetString(AoConfigSection * section,const u8_t * name,u8_t ** value)
{
	AoListHead *head,*__i;
	if(!section||!name)
	{
		*value = NULL;
		return ERR_ARG;
	}
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			*value = NULL;
			return ERR_ARG;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,name));
	*value=((AoConfigKey*)__i)->value;
	return ERR_OK;
	
}
#endif
u8_t *AoSectionGetString(AoConfigSection * section,const u8_t * name)
{
	AoListHead *head,*__i;
	if(!section||!name)
	{
		return NULL;
	}
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			return NULL;
		}
	}while(0!=strcmp((char *)((AoConfigKey*)__i)->name,(char *)name));
	return ((AoConfigKey*)__i)->value;
}
#if 0

int AoConfigGetInteger(AoConfigSection * section,const u8_t * name,int * value)
{
	AoListHead *head,*__i;
	if(!section||!name)
	{
		
		return ERR_ARG;
	}
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			return ERR_ARG;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,name));
	*value=atoi(((AoConfigKey*)__i)->value);
	return ERR_OK;
}

int AoSectionGetInteger(AoConfigSection * section,const u8_t * key)
{
	AoListHead *head,*__i;
	if(!section||!key)
	{
		return 0;
	}
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			return 0;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,key));
	return atoi(((AoConfigKey*)__i)->value);
}

int AoConfigGetUint16(AoConfigSection * section,const u8_t * name,u16_t * value)
{
	AoListHead *head,*__i;
	if(!section||!name)
		return ERR_ARG;
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			return ERR_ARG;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,name));
	*value=(u16_t)atoi(((AoConfigKey*)__i)->value);
	return ERR_OK;
}

int AoConfigGetIpv4(AoConfigSection * section,const u8_t * name,u32_t * value)
{
	AoListHead *head,*__i;
	if(!section||!name)
		return ERR_ARG;
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			return ERR_ARG;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,name));
	AoAddressStringToIpv4(value,((AoConfigKey*)__i)->value);
	return ERR_OK;
	
}


u32_t AoSectionGetIpv4(AoConfigSection * section,const u8_t * key)
{
	AoListHead *head,*__i;
	u32_t value;
	if(!section||!key)
		return 0;
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			return 0;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,key));
	AoAddressStringToIpv4(&value,((AoConfigKey*)__i)->value);
	return value;
}


int AoConfigGetBool(AoConfigSection * section,const u8_t * name,AoBool * value)
{
	AoListHead *head,*__i;
	u8_t * vvv;
	if(!section||!name)
		return ERR_ARG;
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			*value = AO_FALSE;
			return ERR_ARG;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,name));
	vvv = ((AoConfigKey*)__i)->value;
	if (AoStrcasecmp(vvv, "1") == 0
		|| AoStrcasecmp(vvv, "YES") == 0
		|| AoStrcasecmp(vvv, "T") == 0
		|| AoStrcasecmp(vvv, "ON") == 0
		|| AoStrcasecmp(vvv, "AO_TRUE") == 0)
	{
		*value=AO_TRUE;
	}else{
		*value=AO_FALSE;
	}
	return ERR_OK;
	
}

AoBool AoSectionGetBool(AoConfigSection * section,const u8_t * name)
{
	AoListHead *head,*__i;
	u8_t * vvv;
	if(!section||!name)
		return AO_FALSE;
	head = __i=&section->keys;
	do{
		__i=__i->next;
		if(head==__i)
		{
			return AO_FALSE;
		}
	}while(0!=strcmp(((AoConfigKey*)__i)->name,name));
	vvv = ((AoConfigKey*)__i)->value;
	if (AoStrcasecmp(vvv, "1") == 0
		|| AoStrcasecmp(vvv, "YES") == 0
		|| AoStrcasecmp(vvv, "T") == 0
		|| AoStrcasecmp(vvv, "ON") == 0
		|| AoStrcasecmp(vvv, "AO_TRUE") == 0)
	{
		return AO_TRUE;
	}
	return AO_FALSE;
}

int AoConfigSectionDoAll(AoConfigSection * section,SectionFunc func,void * param)
{
	AoListHead *head,*__i;
	AoConfigKey * key;
	if(!section)
		return ERR_ARG;
	head = &section->keys;
	__i= head->next;
	while(__i!=head)
	{
		key = (AoConfigKey *)__i;
		func(key->name,key->value,param);
		__i = __i->next;
	}
	return ERR_OK;
}
int AoConfigDoAll(AoConfig * config,ConfigFunc func,void * param)
{
	AoListHead *head,*__i;
	if(!config)
		return ERR_ARG;
	head = &config->root;
	__i = head->next;

	while(__i!=head)
	{
		if(!AoListHeadEmpty(&((AoConfigSection*)__i)->keys))
		{
			func((AoConfigSection*)__i,param);
		}
		__i = __i->next;
		
	}
	return ERR_OK;

}

static void * AoConfigLoadXmlSection(AoXmlParser xml,AoXmlEvent event,
											const u8_t* szName,
											const u8_t* szAttribute,
											const u8_t* szValue)
											
{
	return AoConfigLoadXmlSection;
}
#endif


static void * AoConfigLoadXmlConfig(AoXmlParser xml,AoXmlEvent event,
											const u8_t* szName,
											const u8_t* szAttribute,
											const u8_t* szValue)
{
	AoConfig * config = (AoConfig *)AoXmlGetAppHandle(xml);
	AoConfigSection * section;
	switch(event)
	{
	case FINISH_TAG:
		section = config->last;
//		printf("del %s\n",szName);
		if(0!=strcmp((char *)section->name,(char *)szName))
			printf("error: %s\n",szName);
		config->last = section->parent;
		break;
	case ADD_ATTRIBUTE:
		if(szAttribute&&szValue)
			AoConfigSetKey(config->last, (u8_t *)szAttribute, (u8_t *)szValue);
		else
			printf("Null attribute %s %s\n",szAttribute,szValue);
		break;
	case FINISH_ATTRIBUTES:
		break;
	case ADD_CONTENT:
		if(szValue&&*szValue)
		{
#if 1
			if((*szValue=='\n'&&*(szValue+1)=='\0')
				||(*szValue=='\r'&&*(szValue+1)=='\0')
				||(*szValue=='\r'&&*(szValue+1)=='\n'&&*(szValue+2)=='\0'))
			{
			}else
#endif
			{
				section = config->last;
				section->value = (u8_t*)memHeapAlloc(&config->home,strlen((char *)szValue)+1);
				strcpy((char *)section->value,(char *)szValue);
			}
		}
		break;
	case ADD_SUBTAG:
//		printf("add %s\n",szName);
		section = AoConfigSetSection(config,szName);
		if(section)
		{
			if(AoListHeadEmpty(&config->root))
			{
				AoListHeadAdd(&section->list,&config->root);
				config->last = section;
				
			}else{
				AoListHeadAdd(&section->list,&config->last->sections);
				section->parent = config->last;
				config->last = section;
			}
		}
		break;
	}
	return AoConfigLoadXmlConfig;
	
}


int AoConfigLoadXml(AoConfig * config,u8_t * buf,int len,SectionLoadFunc func,void * param)
{
	AoXmlParser * xml;
	int res;
	xml = AoXmlCreateParser(buf,len);
	if(!xml)
		return ERR_ARG;
	config->load = func;
	config->param = param;
	AoXmlSetAppHandle(xml,(void *)config);
	res = AoXmlParse(xml,AoConfigLoadXmlConfig);
	AoXmlDestroyParser(xml);
	return res;
}

int AoConfigLoadXmlFromFile(AoConfig * config,u8_t * filename,u8_t *exclude,SectionLoadFunc func,void * param)
{

	int fd;
	struct aos_stat s;
	u8_t * buf;
	int len,ret=0;
	if(!config||(!filename&&!config->filename))
		return ERR_ARG;
	if(!filename)
	{
		filename = config->filename;
	}
	fd = aos_open((char *)filename,O_RDONLY);
	if(fd<0)
	{
		return ERR_MEM;
	}
	

	ret = aos_stat((char *)filename,&s);
	if(ret!=0)
	{
		aos_close(fd);
		return ERR_MEM;
	}
	
	buf = (u8_t*)aos_malloc(s.st_size+1);
	if(buf==NULL)
	{
		aos_close(fd);
		return ERR_MEM;
	}
	ret = aos_read(fd,buf,s.st_size);
	if(ret<0)
	{
		aos_close(fd);
		return ERR_MEM;
	}
	aos_close(fd);
	buf[s.st_size]='\0';
	len = AoConfigLoadXml(config,buf,s.st_size,func,param);
	aos_free(buf);

	return ERR_OK;
}

int	AoSectionWriteXmlToBuffer(AoConfigSection* section,u8_t *buf,int len)
{
	int off = 0,i;
	AoListHead * head,*__i;
	AoConfigKey * key;
	
	AoConfigSection* s = section;

	for(;;)
	{
		head = &s->keys;
		__i = head->prev;
		i = AoSnprintf((char *)&buf[off],len-off,"<%s",s->name);
		if(i<0)
			return ERR_MEM;
		off+=i;
		while(head!=__i)
		{
			key = (AoConfigKey *)__i;
			i = AoSnprintf((char *)&buf[off],len-off," %s=\"%s\"",key->name,key->value);
			if(i<0)
				return ERR_MEM;
			off+=i;
			__i = __i->prev;
		}
	
//		if(!section->parent||__i==&section->parent->sections)
		if(AoListHeadEmpty(&s->sections))
		{
			do{
				if(s->value)
				{
					if(AoListHeadEmpty(&s->sections))
						i = AoSnprintf((char *)&buf[off],len-off,">%s</%s>\r\n",s->value,s->name);
					else
						i = AoSnprintf((char *)&buf[off],len-off,"%s</%s>\r\n",s->value,s->name);
				}
				else{
					if(AoListHeadEmpty(&s->sections))
						i = AoSnprintf((char *)&buf[off],len-off,"/>\r\n");
					else
						i = AoSnprintf((char *)&buf[off],len-off,"</%s>\r\n",s->name);
				}
				if(i<0)
					return ERR_MEM;
				off+=i;
				if(!s->parent||s==section)
				{
					return off;
				}
				__i = &s->list;
				__i = __i->prev;
				s = s->parent;
			}while(__i==&s->sections);
			s = (AoConfigSection* )__i;
			
		}else{

			i = AoSnprintf((char *)&buf[off],len-off,">\r\n");
			if(i<0)
				return ERR_MEM;
			off+=i;
			__i = &s->sections;
			__i = __i->prev;
			s = (AoConfigSection* )__i;
			
		}
	}

	return off;
}

int	AoConfigWriteXmlToBuffer(AoConfig * config,u8_t *buf,int len,u8_t * ex)
{
	if(AoListHeadEmpty(&config->root))
		return ERR_VAL;
		
	return AoSectionWriteXmlToBuffer((AoConfigSection*)config->root.next,buf,len);
}
#if 1
int	AoConfigWriteXmlToFile(AoConfig * config,u8_t * filename,u8_t * ex)
{
	int fd;
	int i;
	char * buf;
//	int i=0;
	
	if(!config||(!filename&&!config->filename))
		return ERR_ARG;

	if(AoListHeadEmpty(&config->root))
		return ERR_VAL;
	if(!filename)
	{
		
		filename = config->filename;
	}
	fd = aos_open((char *)filename,O_WRONLY | O_CREAT| O_TRUNC); //O_CREAT
	if(fd<0)
	{
		printf("Save config open error %s\n",filename);
		return ERR_MEM;
	}
	buf = aos_malloc(64*1024);
	if(!buf)
	{
		aos_close(fd);
		return ERR_MEM;
	}
	i = AoConfigWriteXmlToBuffer(config,(u8_t *)buf,64*1024,ex);
	if(i<0)
	{
		aos_close(fd);
		aos_free(buf);
		return ERR_MEM;
	}
	aos_write(fd,buf,i);
	aos_close(fd);
	aos_free(buf);
	return ERR_OK;
}
#else
int	AoConfigWriteXmlToFile(AoConfig * config,u8_t * filename,u8_t * ex)
{
	int fd;
	int i;
	AoListHead * head,*__i;
	AoConfigKey * key;
	AoConfigSection* s;
//	int i=0;
	
	if(!config||(!filename&&!config->filename))
		return ERR_ARG;

	if(AoListHeadEmpty(&config->root))
		return ERR_VAL;
	if(!filename)
	{
		
		filename = config->filename;
	}
	fd = aos_open(filename,O_WRONLY | O_CREAT | O_TRUNC);
	if(fd<0)
	{
		printf("Save config open error\n");
		return ERR_MEM;
	}
	
	s = (AoConfigSection*)config->root.next;

	for(;;)
	{
		head = &s->keys;
		__i = head->prev;
		if(!s->name)
		{
			aos_close(fd);
			return ERR_VAL;
		}
		if(AoListHeadEmpty(&s->sections)&&!s->value&&head==__i&&s->parent)
		{
			__i = &s->list;
			__i = __i->prev;
			s = s->parent;
			while(__i==&s->sections)
			{
				if(s->value&&s->value[0])
				{
					if(AoListHeadEmpty(&s->sections))
						i = AoFprintf(fd,">%s</%s>\n",s->value,s->name);
					else
						i = AoFprintf(fd,"%s</%s>\n",s->value,s->name);
				}else{
					if(AoListHeadEmpty(&s->sections))
					{
						i = AoFprintf(fd,"/>\n");
					}else{
						i = AoFprintf(fd,"</%s>\n",s->name);
					}
				}
				if(i<0)
				{
					aos_close(fd);
					return ERR_MEM;
				}
				
				if(!s->parent||s==(AoConfigSection*)config->root.next)
				{
					aos_close(fd);
					return ERR_OK;
				}
				__i = &s->list;
				__i = __i->prev;
				s = s->parent;
			}
			s = (AoConfigSection* )__i;
			continue;
		}
		i = AoFprintf(fd,"<%s",s->name);
		if(i<0)
		{
			aos_close(fd);
			return ERR_MEM;
		}
		while(head!=__i)
		{
			key = (AoConfigKey *)__i;
			i = AoFprintf(fd," %s='%s'",key->name,key->value);
			if(i<0)
			{
				aos_close(fd);
				return ERR_MEM;
			}
			
			__i = __i->prev;
		}
	
	
//		if(!section->parent||__i==&section->parent->sections)
		if(AoListHeadEmpty(&s->sections))
		{
			do{
				if(s->value&&s->value[0])
				{
					if(AoListHeadEmpty(&s->sections))
						i = AoFprintf(fd,">%s</%s>\n",s->value,s->name);
					else
						i = AoFprintf(fd,"%s</%s>\n",s->value,s->name);
				}else{
					if(AoListHeadEmpty(&s->sections))
					{
						i = AoFprintf(fd,"/>\n");
					}else{
						i = AoFprintf(fd,"</%s>\n",s->name);
					}
				}
				if(i<0)
				{
					aos_close(fd);
					return ERR_MEM;
				}
				
				if(!s->parent||s==(AoConfigSection*)config->root.next)
				{
					aos_close(fd);
					return ERR_OK;
				}
				__i = &s->list;
				__i = __i->prev;
				s = s->parent;
			}while(__i==&s->sections);
			s = (AoConfigSection* )__i;
			
		}else{

			i = AoFprintf(fd,">\n");
			if(i<0)
			{
				aos_close(fd);
				return ERR_MEM;
			}
			__i = &s->sections;
			__i = __i->prev;
			s = (AoConfigSection* )__i;
			
		}
	}
	aos_close(fd);
	return ERR_OK;
}
#endif

AoConfigSection *AoConfigGetRoot(AoConfig * config)
{
	if(!config)
		return NULL;
	if(AoListHeadEmpty(&config->root))
		return NULL;
	return (AoConfigSection *)config->root.next;
}
AoConfigSection *AoConfigSetRoot(AoConfig * config,u8_t * name)
{
	AoConfigSection * section;
	if(AoListHeadEmpty(&config->root))
	{
		section = AoConfigSetSection(config,name);
		if(!section)
			return NULL;
		AoListHeadAdd(&section->list,&config->root);
		return section;
	}
	return (AoConfigSection *)config->root.next;

}

// AoConfigSection *AoConfigGetNext(AoConfigSection * section)
// {
// 	AoConfig * config = (AoConfig *)section->config;
// 	if(section->link.prev==&config->sections)
// 		return NULL;
// 	return (AoConfigSection*)aos_container_of(section->link.prev,AoConfigSection,link);
// }
AoConfigSection *AoSectionGetSection(AoConfigSection * s,u8_t * name)
{
	AoListHead *head,*__i;
	AoConfigSection * section;
	if(!s||!name)
		return NULL;
	head = &s->sections;
	__i = head->next;
	while(__i!=head)
	{
		section = (AoConfigSection*)__i;
		if(strcmp((char *)section->name,(char *)name)==0)
		{
			return section;
		}
		__i = __i->next;
	}
	return NULL;
}
AoConfigSection *AoSectionSetSection(AoConfigSection * s,u8_t * name)
{
//	AoListHead *head,*__i;
	AoConfigSection * section;
	if(!s||!name)
		return NULL;
	section = AoConfigSetSection(s->config,name);
	if(!section)
		return NULL;
	AoListHeadAdd(&section->list,&s->sections);
	section->parent = s;
	return section;
}
#if 0
AoConfigSection *AoSectionGetFirst(AoConfigSection * section)
{
	if(!section)
		return NULL;
	if(AoListHeadEmpty(&section->sections))
		return NULL;
	return (AoConfigSection *)section->sections.prev;
}

AoConfigSection *AoSectionGetNext(AoConfigSection * section)
{
	if(!section||!section->parent)
		return NULL;
	if(section->list.prev==&section->parent->sections)
		return NULL;
	return  (AoConfigSection *)section->list.prev;
}

AoConfigSection *AoSectionGetParent(AoConfigSection * section)
{
	if(!section)
		return NULL;
	return (AoConfigSection *)section->parent;
}
AoBool AoSectionHaveSection(AoConfigSection * section)
{
	return !AoListHeadEmpty(&section->sections);
}
AoBool AoSectionHaveKey(AoConfigSection * section)
{
	return !AoListHeadEmpty(&section->keys);
}

AoConfigKey * AoKeyGetFirst(AoConfigSection * section)
{
	if(!section)
		return NULL;
	if(AoListHeadEmpty(&section->keys))
		return NULL;
	return (AoConfigKey *)section->keys.prev;
}
AoConfigKey *AoKeyGetNext(AoConfigSection * section,AoConfigKey * key)
{
	if(!key)
		return NULL;
	if(key->list.prev==&section->keys)
		return NULL;
	return  (AoConfigKey *)key->list.prev;
}

int AoSectionKeyCompare(AoConfigSection * s1,AoConfigSection * s2,u8_t *name)
{
	AoConfigKey * key1,*key2;
	AoListHead * head,*__i;
	
	head = &s1->keys;
	__i = head->next;
	while(head!=__i)
	{
		key1 = (AoConfigKey * )__i;
		if(0==strcmp(key1->name,name))
			break;
		__i = __i->next;
	}
	if(head==__i)
		return -1;

	head = &s2->keys;
	__i = head->next;
	while(head!=__i)
	{
		key2 = (AoConfigKey * )__i;
		if(0==strcmp(key2->name,name))
			break;
		__i = __i->next;
	}
	if(head==__i)
		return -1;
	return strcmp(key1->value,key2->value);
}
#endif
int AoSectionKeyUpdate(AoConfigSection * dest,AoConfigSection * src)
{
	AoListHead * head,*__i;
	AoConfigKey * key;


	head = &src->keys;
	__i = head->prev;
#if 0
	if(head==__i&&AoListHeadEmpty(&src->sections))
	{
		AoConfigKeyRemoveAll(dest);
		return ERR_OK;
	}
#endif
	while(head!=__i)
	{
		key = (AoConfigKey *)__i;
		AoConfigReplaceKey(dest,key->name,key->value);
		__i = __i->prev;
	}
	return ERR_OK;
}
#if 0
int AoSectionUpdate(AoConfigSection * dest,AoConfigSection * src)
{
	AoListHead * shead,*__si,*dhead,*__di;
//	AoConfigKey * skey,*dkey;
	AoConfigSection* ds = dest,*ss = src;
	AoConfigSection* stack[128];
	int i=0;

	if(0==strcmp(ds->name,ss->name))
	{
		AoSectionKeyUpdate(ds,ss);
	}
	for(;;)
	{
		shead = &ss->sections;
		__si = shead->next;
		while(shead!=__si)
		{
			dhead = &ds->sections;
			__di = dhead->next;
			while(dhead!=__di)
			{
				if(0==strcmp(((AoConfigSection *)__si)->name,((AoConfigSection *)__di)->name))
				{
					stack[i++]=(AoConfigSection *)__si;
					stack[i++]=(AoConfigSection *)__di;
					AoSectionKeyUpdate((AoConfigSection *)__di,(AoConfigSection *)__si);
					break;
				}
				__di = __di->next;
			}
			if(dhead==__di)
			{
				__di = (AoListHead *)AoSectionSetSection(ds,((AoConfigSection *)__si)->name);
				stack[i++]=(AoConfigSection *)__si;
				stack[i++]=(AoConfigSection *)__di;
				AoSectionKeyUpdate((AoConfigSection *)__di,(AoConfigSection *)__si);
			}
			__si = __si->next;
		}
		if(i<=0)
			break;
		ds = stack[--i];
		ss = stack[--i];
	}

	return ERR_OK;
}
#endif